import { isArray, isNilOrEmpty, isObject, notNilEmpty } from 'qx-util';
import { IPSApplication } from './app/ipsapplication';
import { ModelObjectFillHelp } from './core/utils/model-object-fill-help/model-object-fill-help';
import { IPSModelObject } from './ipsmodel-object';
import { IPSModelService } from './ipsmodel-service';

export abstract class PSModelObjectImpl implements IPSModelObject {
  /**
   * 当前类名称
   *
   * @author chitanda
   * @date 2021-05-26 09:05:15
   * @readonly
   * @type {string}
   */
  get cls(): string {
    return 'PSModelObjectImpl';
  }
  /**
   * 标准模型数据
   *
   * @protected
   * @type {*}
   */
  protected _data: IModel = {};
  /**
   * 当初始化为引用型时，填充的原有引用模型
   *
   * @author chitanda
   * @date 2021-05-21 15:05:51
   * @protected
   * @type {IModel}
   */
  protected _refData: IModel = {};
  /**
   * 模型路径
   *
   * @protected
   * @type {string}
   */
  protected _modelFilePath: string = null;

  /**
   * 是否已填充模型
   *
   * @author chitanda
   * @date 2021-04-11 15:04:14
   * @readonly
   * @type {boolean}
   */
  get isFill(): boolean {
    return notNilEmpty(this._data);
  }

  protected _id: string;

  get id(): string {
    if (this._id) {
      return this._id;
    }
    return this.M.id || this.refM.id;
  }

  get name(): string {
    return this.M.name || this.refM.name;
  }

  get codeName(): string {
    return this.M.codeName || this.refM.codeName;
  }

  get userTag(): string {
    return this.M.userTag;
  }

  get userTag2(): string {
    return this.M.userTag2;
  }

  get userTag3(): string {
    return this.M.userTag3;
  }

  get userTag4(): string {
    return this.M.userTag4;
  }

  get userCat(): string {
    return this.M.userCat;
  }

  get userParams(): IModel {
    return this.M.getUserParam;
  }

  get modelid(): string {
    return this.M.modelid;
  }

  get modeltype(): string {
    return this.M.modeltype;
  }

  /**
   * 模型服务
   *
   * @protected
   * @type {IPSModelService}
   */
  protected iPSModelService: IPSModelService = null;

  /**
   * 父模型
   *
   * @protected
   * @type {IPSModelObject}
   */
  protected parentModel: IPSModelObject = null;

  /**
   * Creates an instance of PSModelObjectImpl.
   * @author chitanda
   * @date 2021-04-06 16:04:48
   */
  constructor() {
    Object.defineProperty(this, 'parentModel', {
      enumerable: false,
      writable: true,
    });
    Object.defineProperty(this, 'iPSModelService', {
      enumerable: false,
      writable: true,
    });
  }

  /**
   * 标准模型
   *
   * @readonly
   * @type {IModel}
   */
  get M(): IModel {
    return this._data;
  }

  get refM(): IModel {
    return this._refData;
  }

  /**
   * 模型文件路径
   *
   * @author chitanda
   * @date 2021-04-08 16:04:16
   * @readonly
   * @type {string}
   */
  get modelFilePath(): string {
    return this._modelFilePath;
  }

  /**
   * 模型标识路径
   *
   * @author chitanda
   * @date 2021-04-08 16:04:08
   * @readonly
   * @type {(string | undefined)}
   */
  get modelPath(): string | undefined {
    return (
      this.M.path ||
      this.refM.path ||
      this.M.dynaModelFilePath ||
      this.modelFilePath
    );
  }

  /**
   * 初始化
   *
   * @param {IPSModelService} iPSModelService 模型服务
   * @param {IPSModelObject} parentModel 父模型
   * @param {string} modelFilePath 模型路径
   * @param {IModel} [data] 模型数据
   */
  init(
    iPSModelService: IPSModelService,
    parentModel: IPSModelObject,
    modelFilePath: string,
    data?: IModel,
    refData?: IModel,
  ) {
    this.iPSModelService = iPSModelService;
    this.parentModel = parentModel;
    this._modelFilePath = modelFilePath;
    if (data != null) {
      this._data = data;
      this.deepFreeze(this._data);
    }
    if (refData != null) {
      this._refData = refData;
      this.deepFreeze(this._refData);
    }
    this.calcTag();
    this.onInit();
  }

  /**
   * 递归设置模型为只读，减少内存使用
   *
   * @author chitanda
   * @date 2022-06-22 10:06:40
   * @protected
   * @param {unknown} data
   */
  protected deepFreeze(data: unknown): void {
    if (data) {
      Object.freeze(data);
      if (isArray(data)) {
        const items = data as unknown[];
        items.forEach(item => {
          if (item && typeof item === 'object') {
            this.deepFreeze(item);
          }
        });
      } else if (isObject(data)) {
        const items = Object.keys(data);
        items.forEach(key => {
          const value = data[key];
          if (value && typeof value === 'object') {
            this.deepFreeze(value);
          }
        });
      }
    }
  }

  protected onInit(): void {}

  /**
   * 计算模型唯一标识
   *
   * @author chitanda
   * @date 2021-07-27 16:07:04
   * @protected
   */
  protected calcTag() {
    if (isNilOrEmpty(this._id)) {
      if (notNilEmpty(this.modelPath)) {
        this._id = this.modelPath;
      } else if (notNilEmpty(this.id)) {
        this._id = this.id;
      } else if (notNilEmpty(this.codeName)) {
        this._id = this.codeName;
      } else if (notNilEmpty(this.name)) {
        this._id = this.name;
      }
    }
  }

  /**
   * 获取模型服务
   *
   * @return {*}  {IPSModelService}
   */
  getPSModelService(): IPSModelService {
    return this.iPSModelService;
  }

  /**
   * 填充自身模型，同时并填充子
   *
   * @author chitanda
   * @date 2021-07-06 18:07:56
   * @param {boolean} [isInitMainChild]
   * @return {*}  {Promise<IPSModelObject>}
   */
  fill(isInitMainChild?: boolean): Promise<IPSModelObject> {
    return this.fillObj(isInitMainChild);
  }

  /**
   * 异步填充标准模型
   *
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async fillObj(isInitMainChild?: boolean): Promise<IPSModelObject> {
    if (!this.isFill) {
      this._data = await this.getPSModelService().getModel(
        this as any,
        this.modelFilePath,
      );
      this.deepFreeze(this._data);
      this.calcTag();
    }
    if (isInitMainChild === true) {
      await ModelObjectFillHelp.fillMainChild(this as any);
    }
    return this as any;
  }

  /**
   * 填充数组模型
   *
   * @protected
   * @param {IModel[]} models
   * @param {string} cls
   * @return {*}  {IPSModelObject[]}
   */
  protected fillChildListModel(
    models: IModel[],
    cls: string,
  ): IPSModelObject[] {
    if (isNilOrEmpty(models)) {
      return;
    }
    return models.map(item => {
      return this.getPSModel4(cls, item, '');
    });
  }

  /**
   * 填充对象模型
   *
   * @protected
   * @param {string} cls
   * @param {IModel} model
   * @return {*}  {IPSModelObject}
   */
  protected fillObjectModel(
    cls: string,
    model: IModel,
    tag: string,
  ): IPSModelObject {
    if (isNilOrEmpty(model)) {
      return;
    }
    return this.getPSModel4(cls, model, tag);
  }

  /**
   * 判断是否实现某接口，子类中发布
   *
   * @param {string} _cls
   * @return {*}  {boolean}
   */
  instanceof(_cls: string): boolean {
    return false;
  }

  /**
   * 从模型节点中获取指定对象
   *
   * @protected
   * @param {string} cls
   * @param {*} objNode
   * @param {string} strTag
   * @return {*}  {*}
   */
  protected getPSModel4(
    cls: string,
    objNode: any,
    strTag: string,
  ): IPSModelObject {
    return this.getPSModelService().getPSModel4(this, cls, objNode, strTag);
  }

  /**
   * 从模型数组中获取指定对象
   *
   * @param cls
   * @param list
   * @param objKey
   * @param bTryMode
   * @returns
   */
  protected getPSModel5(cls: string, list: any[], objKey: any): IPSModelObject {
    return this.getPSModelService().getPSModel5(this, cls, list, objKey);
  }

  getPSApplication(): IPSApplication {
    return this.getPSModelService().getPSApplication();
  }

  getChildPSModelObject(cls: string, objNode: IModel): IPSModelObject {
    return this.getPSModelService().getChildPSModelObject(this, cls, objNode);
  }

  getParentPSModelObject(cls?: string): IPSModelObject {
    if (
      notNilEmpty(cls) &&
      (notNilEmpty(this.parentModel) ||
        this.parentModel.instanceof(cls) !== true)
    ) {
      return this.getPSModelService().getParentPSModelObject(this, cls);
    }
    return this.parentModel;
  }
}
